package alkaid.eis.common.util;

import static org.springframework.util.CollectionUtils.isEmpty;
import static org.springframework.util.StringUtils.hasText;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import org.springframework.util.StringUtils;

import alkaid.eis.common.util.OsHelper.OsType;
import lombok.experimental.UtilityClass;
import lombok.extern.slf4j.Slf4j;

/**
 * shell工具
 * 
 * @author Lucky Yang
 * @since 0.0.1
 */
@Slf4j
@UtilityClass
public class ShellUtil {

    public void execute(List<String> cmdList) throws IOException, InterruptedException {
        execute(cmdList, null);
    }

    public void execute(String commands) throws IOException, InterruptedException {
        execute(commands, null);
    }

    public void execute(String commands, Consumer<ProcessBuilder> customProcessBuilder) throws IOException, InterruptedException {
        if (!hasText(commands)) {
            log.warn("Command line is empty");
            return;
        }

        List<String> cmdList = Stream.of(commands.split(","))
                // 去掉首尾空格
                .map(String::trim)
                // 过滤空串
                .filter(StringUtils::hasText)
                .collect(Collectors.toList());
        execute(cmdList, customProcessBuilder);
    }

    public void execute(List<String> cmdList, Consumer<ProcessBuilder> customProcessBuilder)
            throws IOException, InterruptedException {
        if (isEmpty(cmdList)) {
            log.warn("Command line is empty");
            return;
        }

        List<String> shellLine = getShellLine(cmdList);
        ProcessBuilder builder = new ProcessBuilder(shellLine);
        builder.redirectErrorStream(true);
        if (customProcessBuilder != null) {
            // 客户端自定义配置
            customProcessBuilder.accept(builder);
        }

        if (log.isDebugEnabled()) {
            log.info("Execute command : {}", shellLine);
            log.info("Execute environment : {}", builder.environment());
        }

        Process process = builder.start();
        try {
            if (log.isDebugEnabled()) {
                printProcess(process);
            }
            int exitCode = process.waitFor();
            if (log.isDebugEnabled()) {
                log.info("Execute exit code : {}", exitCode);
            }
        } finally {
            process.destroy();
        }
    }

    private List<String> getShellLine(List<String> cmdList) {
        List<String> rslt = new ArrayList<>(cmdList.size() + 2);
        if (OsType.WINDOWS.equals(OsHelper.OS)) {
            rslt.addAll(List.of("cmd.exe", "/c"));
        } else {
            rslt.addAll(List.of("/bin/sh", "-c"));
        }

        rslt.addAll(cmdList);
        return rslt;
    }

    private void printProcess(Process process) throws IOException {
        ProcessHandle processHandle = process.toHandle();
        ProcessHandle.Info processInfo = processHandle.info();
        log.debug("Execute process info : PID={}, Arguments={}, Command={}, Instant={}, Total CPU duration={}, User={}",
                processHandle.pid(), processInfo.arguments(), processInfo.command(), processInfo.startInstant(),
                processInfo.totalCpuDuration(), processInfo.user());

        // 输出
        try (BufferedReader output = new BufferedReader(new InputStreamReader(process.getInputStream()))) {
            output.lines().forEach(log::info);
        }
    }

}
